package core;

import static view.Constants.BUFFER_SIZE;
import static view.Constants.MAPPING;

import java.io.IOException;
import java.nio.ByteBuffer;

import controller.Controller;

/**
 * @author Mostafa Mahmod Mahmod Eweda
 * <pre>mostafa_eweda17@yahoo.com</pre>
 * @since JDK 1.6
 * @version 1.0
 * 
 * An implementing class of the {@link AbstractCompresser} class that compresses single files
 * 
 * @see CompressedChar
 * @see CompressedBuffer
 * @see AbstractCompresser
 */
public class FileCompresser extends AbstractCompresser
{

	public FileCompresser(String source, String compressed)
	{
		super(source, compressed);
	}

	/**
	 * compress the file using the registered table
	 */
	public void compress()
	{
		CompressedChar[] chars = Controller.getInstance().getChars();
		CompressedBuffer opBuffer = new CompressedBuffer();
		// CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
		try {
			int numRead = 0;
			byte[] buffer = new byte[BUFFER_SIZE];
			int offset = 0;
			long reached = 0;
			while ((numRead = in.read(buffer)) != -1)
			{
				for (int i = 0; i < numRead; i++)
				{
					if (offset == 0)
						offset = opBuffer.add(chars[buffer[i]
								+ MAPPING], chars[buffer[i]
								+ MAPPING].length);
					if (offset != 0)
					{
						// empty buffer in the file
						writer.write(ByteBuffer.wrap(opBuffer.bytes()));
						opBuffer.reset();
						offset = opBuffer.add(chars[buffer[i]
								+ MAPPING], offset);
					}
				}
				reached += buffer.length;
				Controller.getInstance().updateReached(reached, srcFile.length());
			}
			writer.write(ByteBuffer.wrap(opBuffer.bytes(), 0, opBuffer.rows()));
			writer.position(0);
			writer.write(ByteBuffer.wrap(new byte[] { opBuffer
					.getRemainingColumns() }));
			in.close();
			writer.close();
			reached = srcFile.length();
			Controller.getInstance().updateReached(reached, srcFile.length()); // finished compression
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * writes init data without the table
	 */
	public void noTable()
	{
		byte[] data = new byte[2];
		data[0] = 0; // size will be defined in the end
		data[1] = 0; // indicate the table exists
		try {
			writer.write(ByteBuffer.wrap(data));
		} catch (IOException e) {
			e.printStackTrace();
		}
		fileName();
	}

	/**
	 * Saves the name of the source file to be extracted back to its  old extension
	 */
	private void fileName()
	{
		String name = srcFile.getName();
		byte[] buff = new byte[name.length() + 1];
		for (int i = 0; i < buff.length - 1; i++)
			buff[i] = (byte) name.charAt(i);
		buff[buff.length - 1] = 10; // new line feed
		try {
			writer.write(ByteBuffer.wrap(buff));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Writes the table in the file
	 */
	public void writeTable()
	{
		 /* 
		  * Add a one offset for the table marking,
		  * one for the padding and one for the length
		  */
		CompressedChar[] table = Controller.getInstance().getTable();
		int size = table.length * 2 + 3;
		byte[] data = new byte[size];
		data[0] = 0; // size will be defined in the end
		data[1] = 1; // indicate the table exists
		data[2] = (byte) table.length;
		for (int i = 3, n = table.length + 3; i < n; i++)
		{
			CompressedChar cChar = table[i - 3];
			data[2 * i - 3] = cChar.ch;
			data[2 * i - 2] = cChar.length;
		}
		try {
			writer.write(ByteBuffer.wrap(data));
		} catch (IOException e) {
			e.printStackTrace();
		}
		fileName();
	}

	public double getSourceFileSize()
	{
		return srcFile.length();
	}
}
